package com.example.skudemo.core.exception;

import com.example.skudemo.utils.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
    // 最终拦截的错误
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiResult handleException(HttpServletRequest req, Exception e) {
        // 1.获取当前的请求方式
        String method = req.getMethod();
        // 2.获取当前的url
        String requestUrl = req.getRequestURI();
        System.out.println("当前的请求方式:" + method);
        System.out.println("当前的请求url:" + requestUrl);
        // TODO 写入日志中
        Map map = new HashMap<>();
        map.put("请求url", requestUrl);
        map.put("请求方式", method);
        map.put("错误消息", e.getMessage());
        log.error("系统异常", e);
        return ApiResult.error(9999, "服务器端错误", method, requestUrl);
    }

    // 拦截主动抛出的错误
    @ExceptionHandler(value = CommonException.class)
    public ApiResult handleCommonException(HttpServletRequest req, CommonException e) {
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
//        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
        System.out.println(e.getMessage() + "错误信息");
        if (e.getMessage() != null) {
            return ApiResult.error(e.getMessage(), method, requestUrl);
        }
        return ApiResult.error();
    }

    // 拦截post请求参数错误
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ApiResult handleBeanValidation(HttpServletRequest req, MethodArgumentNotValidException e) {
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        // 获取错误列表
        List<ObjectError> errorList = e.getBindingResult().getAllErrors();
        // 获取第一个错误
        String errorMessage = errorList.get(0).getDefaultMessage();
        return ApiResult.error(errorMessage, method, requestUrl);
    }

    // 拦截query和path参数错误
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ApiResult handleConstraintException(HttpServletRequest req, ConstraintViolationException e) {
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        // "test1.name: 参数长度必须是2-10, test1.age: 最大不能超过10", 多个错误的时候是这样的
        String errorMessage = e.getMessage();
        String[] errorList = errorMessage.split(",");
        if (errorList.length != 0) {
            String firstError = errorList[0];
            if (firstError != null) {
                String[] firstErrorList = firstError.split(":");
                if (firstErrorList.length != 0) {
                    return ApiResult.error(firstErrorList[1], method, requestUrl);
                }
            }
        }
        return ApiResult.error(errorMessage, method, requestUrl);
    }

    // 拦截请方式不存在的时候
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    public ApiResult notHandleException(HttpServletRequest req, HttpRequestMethodNotSupportedException e) {
        // 1.获取当前的请求方式
        String method = req.getMethod();
        // 2.获取当前的url
        String requestUrl = req.getRequestURI();
        // TODO 写入日志中
        return ApiResult.error("请求方式不存在", method, requestUrl);
    }

    // 处理参数错误的时候(query的参数采用dto的写法)
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ApiResult bindHandleException(HttpServletRequest req, BindException e) {
        // 1.获取当前的请求方式
        String method = req.getMethod();
        // 2.获取当前的url
        String requestUrl = req.getRequestURI();
        // 3.拼接错误信息
        String message = "";
//        String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
        for (FieldError error : e.getBindingResult().getFieldErrors()) {
            message = error.getDefaultMessage();
            break;
        }

        // TODO 写入日志中
        return ApiResult.error(message, method, requestUrl);
    }

    // 处理参数错误的时候
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ApiResult handleMethodArgumentTypeMismatchException(HttpServletRequest req, MethodArgumentTypeMismatchException e) {
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        return ApiResult.error("参数类型错误", method, requestUrl);
    }

    // 路径不存在的时候
    @ExceptionHandler(NoHandlerFoundException.class)
    public ApiResult handlerNoFoundException(Exception e) {
        log.error(e.getMessage(), e);
        return ApiResult.error("路径不存在，请检查路径是否正确");
    }

    /**
     * 处理sql错误
     *
     * @param e
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public ApiResult bindHandleException(SQLIntegrityConstraintViolationException e) {
        if (e.getMessage().contains("Duplicate entry")) {
            String[] strList = e.getMessage().split(" ");
            String message = strList[2] + "已存在,不能重复添加";
            return ApiResult.error(message);
        }
        return ApiResult.error("未知错误");

    }
}
